<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>小学数学3D多边形旋转系统</title>
    <link href="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-100-M/font-awesome/6.0.0/css/all.min.css"  rel="stylesheet">
    <link href="https://s2.ssl.qhres2.com/static/56662140ef7d5d03.css"  rel="stylesheet">
    <style>
        :root {
            --primary: #3b82f6;
            --secondary: #ef4444;
            --bg-light: #f8fafc;
            --bg-dark: #1e293b;
            --text-primary: #1e293b;
            --text-secondary: #64748b;
        }
 
        body {
            font-family: 'Helvetica Neue', Arial, sans-serif;
            margin: 0;
            padding: 0;
            background-color: var(--bg-light);
            color: var(--text-primary);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            opacity: 0;
            animation: fadeIn 0.5s ease-out forwards;
        }
 
        @keyframes fadeIn {
            to { opacity: 1; }
        }
 
        .container {
            display: flex;
            flex: 1;
            padding: 2rem;
            gap: 2rem;
        }
 
        .control-panel {
            width: 300px;
            background-color: white;
            padding: 2rem;
            border-radius: 0.5rem;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
        }
 
        .display-area {
            flex: 1;
            background-color: white;
            border-radius: 0.5rem;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
            position: relative;
            overflow: hidden;
        }
 
        canvas {
            width: 100%;
            height: 100%;
            display: block;
        }
 
        h1 {
            font-size: 1.5rem;
            font-weight: 600;
            margin-bottom: 1.5rem;
            color: var(--text-primary);
        }
 
        .slider-container {
            margin-bottom: 1.5rem;
        }
 
        label {
            display: block;
            margin-bottom: 0.5rem;
            font-size: 0.875rem;
            color: var(--text-secondary);
        }
 
        input[type="range"] {
            width: 100%;
            height: 6px;
            -webkit-appearance: none;
            background: #e2e8f0;
            border-radius: 3px;
            outline: none;
        }
 
        input[type="range"]::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 16px;
            height: 16px;
            background: var(--primary);
            border-radius: 50%;
            cursor: pointer;
            transition: all 0.2s ease;
        }
 
        input[type="range"]::-webkit-slider-thumb:hover {
            transform: scale(1.2);
        }
 
        .value-display {
            font-size: 0.875rem;
            color: var(--text-primary);
            margin-top: 0.5rem;
            text-align: right;
        }
 
        button {
            width: 100%;
            padding: 0.75rem;
            background-color: var(--primary);
            color: white;
            border: none;
            border-radius: 0.375rem;
            font-size: 0.875rem;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.2s ease;
        }
 
        button:hover {
            background-color: #2563eb;
            transform: translateY(-1px);
        }
 
        button:active {
            transform: translateY(0);
        }
 
        .status {
            margin-top: 1rem;
            font-size: 0.875rem;
            color: var(--text-secondary);
            text-align: center;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="control-panel">
            <h1>3D多边形控制系统</h1>
            
            <div class="slider-container">
                <label for="sides">边数 (3-5):</label>
                <input type="range" id="sides" min="3" max="5" value="3" step="1">
                <div class="value-display" id="sides-value">3</div>
            </div>
            
            <div class="slider-container">
                <label for="angle">旋转角度 (0-360°):</label>
                <input type="range" id="angle" min="0" max="360" value="0">
                <div class="value-display" id="angle-value">0°</div>
            </div>
            
            <button id="rotate-btn">开始旋转</button>
            <div class="status" id="status">准备就绪</div>
        </div>
        
        <div class="display-area">
            <canvas id="canvas"></canvas>
        </div>
    </div>
 
    <script>
        document.addEventListener('DOMContentLoaded',  function() {
            const canvas = document.getElementById('canvas'); 
            const ctx = canvas.getContext('2d'); 
            const sidesSlider = document.getElementById('sides'); 
            const sidesValue = document.getElementById('sides-value'); 
            const angleSlider = document.getElementById('angle'); 
            const angleValue = document.getElementById('angle-value'); 
            const rotateBtn = document.getElementById('rotate-btn'); 
            const statusDisplay = document.getElementById('status'); 
            
            let sides = parseInt(sidesSlider.value); 
            let angle = parseInt(angleSlider.value); 
            let originalPoints = [];
            let selectedEdge = -1;
            let isRotating = false;
            let rotationInterval;
            let polygons = [];
            let isDragging = false;
            let draggedPoint = -1;
            
            // 调整canvas大小 
            function resizeCanvas() {
                const displayArea = document.querySelector('.display-area'); 
                canvas.width  = displayArea.clientWidth; 
                canvas.height  = displayArea.clientHeight; 
                draw();
            }
            
            window.addEventListener('resize',  resizeCanvas);
            resizeCanvas();
            
            // 生成初始多边形 
            function generatePolygon() {
                originalPoints = [];
                const centerX = canvas.width  / 2;
                const centerY = canvas.height  / 2;
                const radius = Math.min(canvas.width,  canvas.height)  * 0.35;
                
                for (let i = 0; i < sides; i++) {
                    const angle = (i * 2 * Math.PI / sides) - Math.PI / 2;
                    originalPoints.push({ 
                        x: centerX + radius * Math.cos(angle), 
                        y: centerY + radius * Math.sin(angle), 
                        isFixed: false 
                    });
                }
                
                polygons = [];
                draw();
            }
            
            // 绘制函数 
            function draw() {
                ctx.clearRect(0,  0, canvas.width,  canvas.height); 
                
                // 绘制旋转生成的多边形（3D效果）
                if (polygons.length  > 0) {
                    const maxDistance = 200; // 最大深度距离 
                    
                    // 根据距离排序多边形以实现正确的3D重叠效果 
                    const sortedPolygons = [...polygons].sort((a, b) => b.distance  - a.distance); 
                    
                    for (const poly of sortedPolygons) {
                        const alpha = 0.6 * (1 - poly.distance  / maxDistance); // 根据距离调整透明度 
                        const lightness = 100 - (poly.distance  / maxDistance * 40); // 根据距离调整亮度 
                        
                        ctx.beginPath(); 
                        ctx.moveTo(poly.points[0].x,  poly.polyPoints[0].y); 
                        
                        for (let i = 1; i < poly.points.length;  i++) {
                            ctx.lineTo(poly.polyPoints[i].x,  poly.polyPoints[i].y); 
                        }
                        
                        ctx.closePath(); 
                        
                        // 创建渐变效果 
                        const gradient = ctx.createLinearGradient(0,  0, 0, canvas.height); 
                        gradient.addColorStop(0,  `hsla(0, 0%, ${lightness}%, ${alpha})`);
                        gradient.addColorStop(1,  `hsla(0, 0%, ${lightness - 20}%, ${alpha})`);
                        
                        ctx.fillStyle  = gradient;
                        ctx.fill(); 
                        
                        ctx.strokeStyle  = `rgba(100, 100, 100, ${alpha * 0.5})`;
                        ctx.lineWidth  = 1;
                        ctx.stroke(); 
                    }
                }
                
                // 绘制原始多边形 
                if (originalPoints.length  > 0) {
                    ctx.beginPath(); 
                    ctx.moveTo(originalPoints[0].x,  originalPoints[0].y);
                    
                    for (let i = 1; i < originalPoints.length;  i++) {
                        ctx.lineTo(originalPoints[i].x,  originalPoints[i].y);
                    }
                    
                    ctx.closePath(); 
                    
                    // 绘制填充 
                    ctx.fillStyle  = 'rgba(59, 130, 246, 0.3)';
                    ctx.fill(); 
                    
                    // 绘制边 
                    for (let i = 0; i < originalPoints.length;  i++) {
                        const next = (i + 1) % originalPoints.length; 
                        
                        ctx.beginPath(); 
                        ctx.moveTo(originalPoints[i].x,  originalPoints[i].y);
                        ctx.lineTo(originalPoints[next].x,  originalPoints[next].y);
                        
                        if (selectedEdge === i) {
                            ctx.strokeStyle  = 'rgba(239, 68, 68, 1)';
                            ctx.lineWidth  = 3;
                        } else {
                            ctx.strokeStyle  = 'rgba(59, 130, 246, 1)';
                            ctx.lineWidth  = 2;
                        }
                        
                        ctx.stroke(); 
                    }
                    
                    // 绘制顶点 
                    for (let i = 0; i < originalPoints.length;  i++) {
                        ctx.beginPath(); 
                        ctx.arc(originalPoints[i].x,  originalPoints[i].y, 6, 0, Math.PI * 2);
                        ctx.fillStyle  = 'rgba(59, 130, 246, 1)';
                        ctx.fill(); 
                        ctx.strokeStyle  = 'white';
                        ctx.lineWidth  = 2;
                        ctx.stroke(); 
                    }
                }
            }
            
            // 计算两点之间的距离 
            function distance(x1, y1, x2, y2) {
                return Math.sqrt(Math.pow(x2  - x1, 2) + Math.pow(y2  - y1, 2));
            }
            
            // 找到最近的边 
            function findClosestEdge(x, y) {
                let minDistance = Infinity;
                let closestEdge = -1;
                
                for (let i = 0; i < originalPoints.length;  i++) {
                    const next = (i + 1) % originalPoints.length; 
                    const x1 = originalPoints[i].x;
                    const y1 = originalPoints[i].y;
                    const x2 = originalPoints[next].x;
                    const y2 = originalPoints[next].y;
                    
                    // 计算点到线段的距离 
                    const d = pointToLineDistance(x, y, x1, y1, x2, y2);
                    
                    if (d < minDistance && d < 15) { // 15像素的点击范围 
                        minDistance = d;
                        closestEdge = i;
                    }
                }
                
                return closestEdge;
            }
            
            // 计算点到线段的距离 
            function pointToLineDistance(x, y, x1, y1, x2, y2) {
                const A = x - x1;
                const B = y - y1;
                const C = x2 - x1;
                const D = y2 - y1;
                
                const dot = A * C + B * D;
                const len_sq = C * C + D * D;
                let param = -1;
                
                if (len_sq !== 0) {
                    param = dot / len_sq;
                }
                
                let xx, yy;
                
                if (param < 0) {
                    xx = x1;
                    yy = y1;
                } else if (param > 1) {
                    xx = x2;
                    yy = y2;
                } else {
                    xx = x1 + param * C;
                    yy = y1 + param * D;
                }
                
                return distance(x, y, xx, yy);
            }
            
            // 找到最近的点 
            function findClosestPoint(x, y) {
                let minDistance = Infinity;
                let closestPoint = -1;
                
                for (let i = 0; i < originalPoints.length;  i++) {
                    const d = distance(x, y, originalPoints[i].x, originalPoints[i].y);
                    if (d < minDistance && d < 15) { // 15像素的拖动范围 
                        minDistance = d;
                        closestPoint = i;
                    }
                }
                
                return closestPoint;
            }
            
            // 旋转多边形 
            function rotatePolygon() {
                if (selectedEdge === -1 || originalPoints.length  === 0) {
                    statusDisplay.textContent  = "请先选择一条边作为旋转轴";
                    return;
                }
                
                isRotating = true;
                rotateBtn.disabled  = true;
                sidesSlider.disabled  = true;
                angleSlider.value  = 0;
                angleValue.textContent  = "0°";
                polygons = [];
                
                statusDisplay.textContent  = "旋转中...";
                
                let currentAngle = 0;
                const step = 1; // 每次增加1度 
                const totalSteps = 360 / step;
                
                // 获取旋转轴的起点和终点 
                const axisStart = originalPoints[selectedEdge];
                const axisEnd = originalPoints[(selectedEdge + 1) % originalPoints.length]; 
                
                // 计算旋转轴的方向向量 
                const axisDir = {
                    x: axisEnd.x - axisStart.x,
                    y: axisEnd.y - axisStart.y,
                    z: 0 
                };
                
                // 归一化方向向量 
                const length = Math.sqrt(axisDir.x  * axisDir.x + axisDir.y * axisDir.y);
                axisDir.x /= length;
                axisDir.y /= length;
                
                rotationInterval = setInterval(() => {
                    currentAngle += step;
                    angleSlider.value  = currentAngle;
                    angleValue.textContent  = `${currentAngle}°`;
                    
                    if (currentAngle >= 360) {
                        clearInterval(rotationInterval);
                        isRotating = false;
                        rotateBtn.disabled  = false;
                        sidesSlider.disabled  = false;
                        statusDisplay.textContent  = "旋转完成";
                        return;
                    }
                    
                    // 计算当前角度的弧度值 
                    const radians = currentAngle * Math.PI / 180;
                    
                    // 旋转矩阵（绕任意轴旋转）
                    const u = axisDir.x;
                    const v = axisDir.y;
                    const w = axisDir.z;
                    
                    const cos = Math.cos(radians); 
                    const sin = Math.sin(radians); 
                    const oneMinusCos = 1 - cos;
                    
                    // 旋转矩阵 
                    const rotationMatrix = [
                        [
                            u * u * oneMinusCos + cos,
                            u * v * oneMinusCos - w * sin,
                            u * w * oneMinusCos + v * sin 
                        ],
                        [
                            v * u * oneMinusCos + w * sin,
                            v * v * oneMinusCos + cos,
                            v * w * oneMinusCos - u * sin 
                        ],
                        [
                            w * u * oneMinusCos - v * sin,
                            w * v * oneMinusCos + u * sin,
                            w * w * oneMinusCos + cos 
                        ]
                    ];
                    
                    // 创建旋转后的多边形 
                    const rotatedPoints = [];
                    const centerX = canvas.width  / 2;
                    const centerY = canvas.height  / 2;
                    
                    for (let i = 0; i < originalPoints.length;  i++) {
                        // 将点转换为相对于旋转轴的坐标 
                        let x = originalPoints[i].x - axisStart.x;
                        let y = originalPoints[i].y - axisStart.y;
                        let z = 0;
                        
                        // 应用旋转矩阵 
                        const newX = rotationMatrix[0][0] * x + rotationMatrix[0][1] * y + rotationMatrix[0][2] * z;
                        const newY = rotationMatrix[1][0] * x + rotationMatrix[1][1] * y + rotationMatrix[1][2] * z;
                        const newZ = rotationMatrix[2][0] * x + rotationMatrix[2][1] * y + rotationMatrix[2][2] * z;
                        
                        // 转换回绝对坐标 
                        rotatedPoints.push({ 
                            x: newX + axisStart.x,
                            y: newY + axisStart.y,
                            z: newZ 
                        });
                    }
                    
                    // 计算多边形到观察者的距离（用于3D效果）
                    let avgZ = 0;
                    for (const point of rotatedPoints) {
                        avgZ += point.z;
                    }
                    avgZ /= rotatedPoints.length; 
                    
                    // 应用透视效果 
                    const perspectivePoints = [];
                    const focalLength = 800; // 焦距 
                    
                    for (const point of rotatedPoints) {
                        // 计算透视比例 (1 / (1 + z / focalLength))
                        const scale = focalLength / (focalLength + point.z);
                        perspectivePoints.push({ 
                            x: centerX + (point.x - centerX) * scale,
                            y: centerY + (point.y - centerY) * scale 
                        });
                    }
                    
                    // 保存多边形（包括原始点和透视点）
                    polygons.push({ 
                        points: rotatedPoints,
                        polyPoints: perspectivePoints,
                        distance: avgZ, // 用于深度排序 
                        angle: currentAngle 
                    });
                    
                    draw();
                }, 20); // 每20毫秒更新一次 
            }
            
            // 事件监听器 
            sidesSlider.addEventListener('input',  function() {
                sides = parseInt(this.value); 
                sidesValue.textContent  = sides;
                generatePolygon();
            });
            
            angleSlider.addEventListener('input',  function() {
                angle = parseInt(this.value); 
                angleValue.textContent  = `${angle}°`;
                
                if (!isRotating) {
                    draw();
                }
            });
            
            rotateBtn.addEventListener('click',  rotatePolygon);
            
            canvas.addEventListener('click',  function(e) {
                if (isRotating) return;
                
                const rect = canvas.getBoundingClientRect(); 
                const x = e.clientX  - rect.left; 
                const y = e.clientY  - rect.top; 
                
                selectedEdge = findClosestEdge(x, y);
                draw();
            });
            
            canvas.addEventListener('mousedown',  function(e) {
                if (isRotating || selectedEdge !== -1) return;
                
                const rect = canvas.getBoundingClientRect(); 
                const x = e.clientX  - rect.left; 
                const y = e.clientY  - rect.top; 
                
                draggedPoint = findClosestPoint(x, y);
                if (draggedPoint !== -1) {
                    isDragging = true;
                }
            });
            
            canvas.addEventListener('mousemove',  function(e) {
                if (!isDragging || draggedPoint === -1 || isRotating) return;
                
                const rect = canvas.getBoundingClientRect(); 
                const x = e.clientX  - rect.left; 
                const y = e.clientY  - rect.top; 
                
                originalPoints[draggedPoint].x = x;
                originalPoints[draggedPoint].y = y;
                draw();
            });
            
            canvas.addEventListener('mouseup',  function() {
                isDragging = false;
                draggedPoint = -1;
            });
            
            canvas.addEventListener('mouseleave',  function() {
                isDragging = false;
                draggedPoint = -1;
            });
            
            // 初始化 
            generatePolygon();
        });
    </script>
</body>
</html>